#include <los_sys.h>
#include <linux/kernel.h>
#include <fcntl.h>

#include "hirandom.h"

static unsigned long random_max =  0x7FFFFFFF;

static long do_rand(unsigned long *value)
{
    long quotient, remainder, t;

    quotient = *value / 127773L;
    remainder = *value % 127773L;
    t = 16807L * remainder - 2836L * quotient;
    if (t <= 0) {
        t += 0x7fffffff;
    }
    return ((*value = t) % (random_max + 1));
}

static unsigned long seed = 1;

int ran_open(struct file *filep)
{
    seed = (unsigned long)(hi_sched_clock() & 0xffffffff);
    return 0;
}

static int ran_close(struct file *filep)
{
    return 0;
}

int ran_ioctl(struct file *filep, int cmd, unsigned long arg)
{
    switch (cmd) {
        case RANDOM_SET_MAX:
            random_max = arg;
            break;
        default:
            PRINT_ERR("!!!bad command!!!\n");
            return -EINVAL;
    }
    return ENOERR;
}

ssize_t ran_read(struct file *filep, char *buffer, size_t buflen)
{
    ssize_t len = buflen;
    char *buf = buffer;
    if (len % sizeof(unsigned int)) {
        PRINT_ERR("random size not aligned by 4 bytes\n");
        return -EINVAL;
    }
    while (len > 0) {
        *(unsigned int *)buf = do_rand(&seed);
        len -= sizeof(unsigned int);
        buf += sizeof(unsigned int);
    }
    return buflen;
}

static const struct file_operations_vfs ran_dev_ops = {
    ran_open,  /* open */
    ran_close, /* close */
    ran_read,  /* read */
    NULL, /* write */
    NULL,          /* seek */
    ran_ioctl,  /* ioctl */
#ifndef CONFIG_DISABLE_POLL
    0, /* poll */
#endif
    NULL
};

int ran_dev_register(void)
{
    return register_driver("/dev/urandom", &ran_dev_ops, 0666, 0);
}

